import math

import cv2
import numpy as np

original_image = cv2.imread("pic/tongxing.jpg")

h, w, t=original_image.shape
cv2.namedWindow('original',0)
cv2.resizeWindow('original',w,h)
cv2.imshow("original", original_image)
image = original_image.copy()
'''
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
cv2.imshow("blurred", blurred)
canny = cv2.Canny(blurred, 120, 255, 1)
cv2.imshow("canny", canny)
'''

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 中值滤波平滑，消除噪声
# 当图片缩小后，中值滤波的孔径也要相应的缩小，否则会将有效的轮廓擦除
binary = cv2.medianBlur(gray,5)
#转换为二值图像
ret, canny = cv2.threshold(binary, 120, 255, cv2.THRESH_BINARY)
cv2.imshow("binary", canny)
#再次检测边缘
canny = cv2.Canny(canny, 120, 255, 1)
cv2.imshow("canny", canny)
contours,_ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

rho_e=100
theta_e=np.pi/180*10
rho_history=[]
theta_history=[]
lines = cv2.HoughLines(canny, 1, np.pi / 180, 80, None, 0, 0)#minLineLength: 最小直线长度，小于该长度的直线会被排除。maxLineGap: 直线之间允许的最大间隙，小于该间隙的直线将被连接为一条直线。

img = original_image.copy()
# 在原图上绘制检测到的直线
for line in lines:
    rho, theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * (a))
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * (a))
    cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imwrite('pic/huofu.png', img)

line=lines[0]
rho,theta=line[0]#每条直线的极径以及极角(和x轴夹角)
a=np.cos(theta)
b=np.sin(theta)
x0 = a * rho
y0 = b * rho
x1=int(x0+1000*(-b))#直线上另外两点，
y1=int(y0+1000*a)
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
cv2.line(original_image,(x1,y1),(x2,y2),(0,255,0),2)
rho_history.append(rho)
theta_history.append(theta)
for line in lines[1:]:

    flag=0
    rho,theta=line[0]
    for i in range(len(theta_history)):
        if np.abs(rho-rho_history[i])<rho_e and np.abs(theta-theta_history[i])<theta_e:#过滤平行的直线，只保留各个相互垂直的矩形边
            flag=1
            break
    if flag==1:
        continue

    rho, theta = line[0]
    a=np.cos(theta)
    b=np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1=int(x0+1000*(-b))
    y1=int(y0+1000*a)
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * a)
    cv2.line(original_image,(x1,y1),(x2,y2),(0,255,0),2)
    rho_history.append(rho)
    theta_history.append(theta)
print(rho_history)
print(theta_history)
cv2.imwrite('pic/detected_huofu1.png', original_image)
'''
cv2.namedWindow('huofuzhixian',0)
cv2.resizeWindow('huofuzhixian',w,h)
cv2.imshow("huofuzhixian", original_image)
cv2.imwrite('pic/detected_huofu1.png', original_image)
cv2.waitKey(0)
'''
def boundingBox(idx, c):
    if len(c) < 2:
        return None
    epsilon = 30
    while True:
        approxBox = cv2.approxPolyDP(c, epsilon, True)
        # 求出拟合得到的多边形的面积,连通区域，用格林公式
        theArea = math.fabs(cv2.contourArea(approxBox))
        # 输出拟合信息
        print("contour idx: %d ,contour_len: %d ,epsilon: %d ,approx_len: %d ,approx_area: %s" % (
        idx, len(c), epsilon, len(approxBox), theArea))
        if (len(approxBox) < 4):
            return None
        if theArea > 0:
            if (len(approxBox) > 4):
                # epsilon 增长一个步长值
                epsilon += 1
                continue
            else:  # approx的长度为4，表明已经拟合成矩形了
                # 转换成4*2的数组
                approxBox = approxBox.reshape((4, 2))
                return approxBox
        else:
            print("failed to find boundingBox,idx = %d area=%f" % (idx, theArea))
            return None

def order_points(pts):
    # 初始化将要排序的坐标列表
    # 所以列表中的第一个条目是左上角，
    # 第二条是右上角，第三条是右下角，第四条是左下角
    rect = np.zeros((4, 2), dtype="int")

    # 左上角的和最小，而右下角的和最大
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    # 计算两个点之间的差异，右上角的差异最小，而左下角的差异最大，点的x,y做差，y-x，右上角为负数，左下角为正数，其余接近0
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    # [top-left, top-right, bottom-right, bottom-left]
    return rect

def fx(rho,theta):
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * a)
    k=(y1-y0)/(x1-x0)
    return [x0,y0,k]
def find_point(fx1,fx2):
    x=(fx2[1]-fx1[1]+fx1[2]*fx1[0]-fx2[2]*fx2[0])/(fx1[2]-fx2[2])
    y=fx1[1]+fx1[2]*(x-fx1[0])
    if x<w:
        return [x,y]
    else:
        return 0
flag=0
point=[]
fx0=fx(rho_history[0],theta_history[0])#取第一根直线作为基准
for i in range(1,4):
    fx1=fx(rho_history[i],theta_history[i])
    if find_point(fx0, fx1) ==0:#两线平行无相交点，记录和基准线平行的另外矩形的边
        flag=i
    else:
        point.append(find_point(fx0, fx1))#找到两根直线相交点则进入point
fx0=fx(rho_history[flag],theta_history[flag])#对于平行的边
for i in range(1,4):
    if i==flag:#对除了基准线外的线，找另外两根线和前一步找到的平行线的交点
        continue
    else:
        fx1 = fx(rho_history[i], theta_history[i])
        point.append(find_point(fx0, fx1))
BOX = order_points(np.array(point))#排序四个点，获得四边形
BOX_result=BOX
print(np.array(point))
'''
color=(36, 255, 12)
thickness=2
cv2.line(image, tuple(BOX[0]), tuple(BOX[1]), color, thickness)
cv2.line(image, tuple(BOX[1]), tuple(BOX[2]), color, thickness)
cv2.line(image, tuple(BOX[2]), tuple(BOX[3]), color, thickness)
cv2.line(image, tuple(BOX[3]), tuple(BOX[0]), color, thickness)
#print("boundingBox：\n", box)
#cv2.imwrite('pic/detected_yanmobianhuan.png', image)
cv2.namedWindow('detected',0)
cv2.resizeWindow('detected',w,h)
cv2.imshow("detected", image)
'''


s=0
BOX1=np.zeros((4, 2), dtype="int")
for idx, c in enumerate(contours):
    approxBox = boundingBox(idx, c)
    if approxBox is None:
        #        print("\n")
        continue

    # 获取最小矩形包络
    rect = cv2.minAreaRect(approxBox)# 得到最小外接矩形的（中心(x,y), (宽,高), 旋转角度），approxBox是array对象，任意点集
    box = cv2.boxPoints(rect)# 获取最小外接矩形的4个顶点坐标
    box = box.reshape(4, 2)
    box = order_points(box)
    if cv2.contourArea(box)>s:
        s=cv2.contourArea(box)
        BOX1=box
BOX = BOX.astype(np.float32)
BOX1 = BOX1.astype(np.float32)#检测到的原先四边形的最小矩形包络
M = cv2.getPerspectiveTransform(BOX,BOX1)#掩膜变换比例

result_img_22 = cv2.warpPerspective(image, M, (w, h))#注意是src图像的高宽，因为要和src作掩膜，高宽要一致
cv2.imshow('result_img_22',result_img_22)
#cv2.imwrite('pic/tongxing_jiaozheng.png', result_img_22)
BOX1 = BOX1.astype(np.int32)
cropped = result_img_22[BOX1[0][1]:BOX1[2][1], BOX1[0][0]:BOX1[2][0]] # 裁剪坐标为[y0:y1, x0:x1]
cv2.imwrite('pic/tongxing_fenge2.png', cropped)

cv2.waitKey(0)